<script lang="ts" setup>
import {computed, ref, watch} from "vue"
import {useWindowSize} from '@vueuse/core'
import {FormRules} from "naive-ui"
import {ResType, ResObjPropNameConst} from "st-common-req"
import {
  StnIconifyIconSelector,
  useTipStore,
  TipMessageTypeConst,
  StnIpt,
  IptTypeConst,
} from "st-ui-vue3-navie"
import {isNone} from 'st-common-core'
import {
  saveMenu,
  MenuIcon,
  useMenuStore,
  MenuType,
  MenuTypeEnum,
  MenuIconTypeEnum,
  MenuIconTypeLabelEnum,
  MenuObjPropNameEnum,
} from '@menu'
import {useLoadingStore} from '@loading'
import {IconifyIconSets} from '@icon'
import {ResponsiveDialogForm} from '@components'
import {FormItemShowedMap} from "../consts/menu-form-item-show.ts"
import {FormRulesMap} from "../consts/menu-form-rules.ts"
import {MenuFormDefault} from '../consts/menu-form-default.ts'
import {FormVerifyMap} from '../utils/menu-form-verify-util.ts'
import {useSysMenuStore} from '../stores/sys-menu-store.ts'
import {MenuIptPropsPathEnum} from '../enums/menu-ipt-props-path-enum.ts'

/**
 * 组件参数
 */
const {
  isAdd = false,
} = defineProps<{
  // 是否为新增菜单信息
  isAdd?: boolean;
}>()

/**
 * 组件双向数据绑定
 */
// 编辑对话框是否显示
const show = defineModel<boolean>('show', {default: false})
// 菜单信息表单数据
const form = defineModel<Record<string, any>>('form', {default: {...MenuFormDefault}})

/**
 * 监听菜单类型的变化，改变显示的表单项及表单信息输入规则
 */
// 显示的表单项
const formItemShowed = ref<Record<string, boolean>>({})
// 表单信息输入规则
const formRules = ref<FormRules>({})
watch(() => form.value.type, (type: MenuTypeEnum) => {
  formItemShowed.value = FormItemShowedMap[type]
  formRules.value = FormRulesMap[type]
}, {immediate: true})

/**
 * 路由路径相关处理
 */
// 路由路径自动填充选项
const routePathAutoCompleteOptions = computed(() => {
  const parentId = form.value[MenuObjPropNameEnum.PARENT_ID]
  if (isNone(parentId)) return []
  // 路由路径前缀（父级的路由路径）
  let routePathPre = useMenuStore().getMenuDataMap()[parentId][MenuObjPropNameEnum.ROUTE_PATH]
  routePathPre = routePathPre ? routePathPre : ''
  return [
    {
      label: routePathPre + form.value[MenuObjPropNameEnum.ROUTE_PATH],
      value: routePathPre + form.value[MenuObjPropNameEnum.ROUTE_PATH],
    }
  ]
})

// 路由参数列表
const routeQueries = ref<Array<[string, string]>>([])

// 是否显示图标选择器弹框
const isShowIconSelectorPop = ref<boolean>(false)
/**
 * 图标类型改变时的处理函数
 */
const iconTypeChangeHandler = () => {
  isShowIconSelectorPop.value = form.value[MenuObjPropNameEnum.ICON_TYPE] === MenuIconTypeEnum.ICONIFY
}
/**
 * 监听屏幕宽度，响应式改变图标选择器布局
 */
// 图标选择器的宽度
const iconSelectorWidth = ref<string>('auto')
// 图标选择器的页码个数
const iconSelectorPageSlotCnt = ref<number>(10)
// 图标选择器显示快速跳转器
const iconSelectorShowQuickJumper = ref(true)
// 图标选择器分页器是否使用简单分页
const iconSelectorPaginationSimple = ref(false)
// 监听屏幕宽度的变化
watch(() => useWindowSize().width.value, (width) => {
  if (width <= 768) {
    iconSelectorWidth.value = '55vw'
    iconSelectorPaginationSimple.value = true
    iconSelectorShowQuickJumper.value = false
  } else {
    iconSelectorWidth.value = 'auto'
    iconSelectorPaginationSimple.value = false
    iconSelectorShowQuickJumper.value = true
  }
  if (width <= 560) {
    iconSelectorWidth.value = '70vw'
  }
}, {immediate: true})

/**
 * 提交表单处理函数
 */
const submitHandler = async () => {
  /**
   * 校验表单数据
   */
  useLoadingStore().setLoadingDesc('校验表单数据...')
  // 根据菜单类型获取表单项校验处理函数
  const formVerify = FormVerifyMap[form.value[MenuObjPropNameEnum.TYPE]]
  // 遍历表单项校验函数
  for (const key in formVerify) {
    // 如果有表单项校验不通过，提示错误信息并退出表单的提交
    if (!(await formVerify[key](form.value))) {
      return Promise.reject({
        [ResObjPropNameConst.STATUS]: false,
        [ResObjPropNameConst.MESSAGE]: formRules.value[key]['message'],
      })
    }
  }
  // 如果填写了路由参数，则对其进行校验，并将其转化为路由 Query 参数字符串
  if (form.value[MenuObjPropNameEnum.TYPE] === MenuTypeEnum.MENU && routeQueries.value.length > 0) {
    // 路由 Query 参数字符串
    let queryStr = ''
    // 处理到第几个 Query 参数数组
    let queryArrCnt = 0
    for (const queryArr of routeQueries.value) {
      // 路由参数名称不能为空
      if (queryArr[0] === undefined || queryArr[0] === null || queryArr[0].length <= 0) {
        return Promise.reject({
          [ResObjPropNameConst.STATUS]: false,
          [ResObjPropNameConst.MESSAGE]: '路由参数的名称不能为空',
        })
      }
      queryArrCnt++
      if (queryArrCnt === 1) queryStr += '?'
      else queryStr += '&'
      queryStr += queryArr[0] + '=' + queryArr[1]
    }
    form.value[MenuObjPropNameEnum.ROUTE_QUERY] = queryStr
  }
  /**
   * 发送请求保存菜单信息
   */
  useLoadingStore().setLoadingDesc('保存菜单信息...')
  return await saveMenu(form.value as MenuType)
    .then(async (res: ResType) => {
      // 保存失败
      if (!res[ResObjPropNameConst.STATUS]) return Promise.reject(res)
      // 保存成功
      useTipStore().message(res[ResObjPropNameConst.MESSAGE], TipMessageTypeConst.SUCCESS)
      /**
       * 获取新的菜单信息列表
       */
      useLoadingStore().setLoadingDesc('更新表中菜单信息...')
      const resGetMenus = await useSysMenuStore().getTableDataByReq()
      // 获取新的菜单信息列表失败
      if (!resGetMenus[ResObjPropNameConst.STATUS]) return Promise.reject(res)
    })
}
</script>

<template>
  <div class="menu-info-editor-dialog">
    <ResponsiveDialogForm
      :title="isAdd ? '新增菜单' : '编辑菜单'"
      v-model:show="show"
      v-model:form="form"
      :rules="formRules"
      :submit-handler="submitHandler"
    >
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.PARENT_ID]" :span="2">
        <StnIpt
          v-model:value="form[MenuObjPropNameEnum.PARENT_ID]"
          :type="IptTypeConst.CASCADER"
          :predefine-props="MenuIptPropsPathEnum.PARENT_ID"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.TYPE]">
        <StnIpt
          v-model:value="form[MenuObjPropNameEnum.TYPE]"
          :type="IptTypeConst.RADIO_BTN_GROUP"
          :predefine-props="MenuIptPropsPathEnum.TYPE"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.NAME]">
        <StnIpt
          v-model:value="form[MenuObjPropNameEnum.NAME]"
          :predefine-props="MenuIptPropsPathEnum.NAME"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.PAGE_PATH]">
        <StnIpt
          v-model:value="form[MenuObjPropNameEnum.PAGE_PATH]"
          :type="IptTypeConst.SELECT"
          :predefine-props="MenuIptPropsPathEnum.PAGE_PATH_SELECT"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.PAGE_PATH]">
        <n-popover trigger="hover" :disabled="!form[MenuObjPropNameEnum.PAGE_PATH]">
          <template #trigger>
            <StnIpt
              :value="form[MenuObjPropNameEnum.PAGE_PATH]"
              :predefine-props="MenuIptPropsPathEnum.PAGE_PATH_INPUT"
              :props="{ipt: {disabled: true}}"
            />
          </template>
          <span>{{ form[MenuObjPropNameEnum.PAGE_PATH] }}</span>
        </n-popover>
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.ROUTE_NAME]">
        <StnIpt
          v-model:value="form[MenuObjPropNameEnum.ROUTE_NAME]"
          :predefine-props="MenuIptPropsPathEnum.ROUTE_NAME"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.ROUTE_PATH]">
        <StnIpt
          v-model:value.trim.lazy="form[MenuObjPropNameEnum.ROUTE_PATH]"
          :type="IptTypeConst.AUTO_COMPLETE"
          :predefine-props="MenuIptPropsPathEnum.ROUTE_PATH"
          :props="{ipt: {options: routePathAutoCompleteOptions}}"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.ROUTE_QUERY]" :span="2">
        <StnIpt
          v-model:value="routeQueries"
          :type="IptTypeConst.LIST_INPUT"
          :predefine-props="MenuIptPropsPathEnum.ROUTE_QUERY"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.LINK_PATH]" :span="2">
        <StnIpt
          v-model:value="form[MenuObjPropNameEnum.LINK_PATH]"
          :predefine-props="MenuIptPropsPathEnum.LINK_PATH"
        />
      </n-gi>
      <n-form-item-gi v-if="formItemShowed[MenuObjPropNameEnum.ICON_TYPE]" label="图标类型" path="iconType">
        <n-popover
          trigger="manual"
          :show="isShowIconSelectorPop"
          @clickoutside="() => isShowIconSelectorPop = false"
          placement="bottom-start"
          display-directive="show"
          :show-arrow="false"
          :flip="false"
        >
          <template #trigger>
            <n-radio-group
              v-model:value="form[MenuObjPropNameEnum.ICON_TYPE]"
              @update:value="iconTypeChangeHandler"
            >
              <n-radio-button
                :label="MenuIconTypeLabelEnum.ICONIFY"
                :value="MenuIconTypeEnum.ICONIFY"
                @click="() => isShowIconSelectorPop = true"
              />
              <n-radio-button :label="MenuIconTypeLabelEnum.TEXT" :value="MenuIconTypeEnum.TEXT"/>
              <n-radio-button :label="MenuIconTypeLabelEnum.LINK" :value="MenuIconTypeEnum.LINK"/>
            </n-radio-group>
          </template>
          <div :style="{width: iconSelectorWidth}">
            <StnIconifyIconSelector
              :sets="IconifyIconSets"
              v-model:value="form[MenuObjPropNameEnum.ICON]"
              :page-slot="iconSelectorPageSlotCnt"
              :show-quick-jumper="iconSelectorShowQuickJumper"
              :pagination-simple="iconSelectorPaginationSimple"
            />
          </div>
        </n-popover>
      </n-form-item-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.ICON]">
        <StnIpt
          v-model:value="form[MenuObjPropNameEnum.ICON]"
          :predefine-props="MenuIptPropsPathEnum.ICON"
        >
          <template #suffix>
            <MenuIcon
              class="ml-2"
              :icon="form[MenuObjPropNameEnum.ICON]"
              :icon-type="form[MenuObjPropNameEnum.ICON_TYPE]"
            />
          </template>
        </StnIpt>
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.SORT]">
        <StnIpt
          v-model:value="form[MenuObjPropNameEnum.SORT]"
          :type="IptTypeConst.INPUT_NUMBER"
          :predefine-props="MenuIptPropsPathEnum.SORT"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.DISABLED]">
        <StnIpt
          :value="!form[MenuObjPropNameEnum.DISABLED]"
          @update:value="() => form[MenuObjPropNameEnum.DISABLED] = !form[MenuObjPropNameEnum.DISABLED]"
          :type="IptTypeConst.SWITCH"
          :predefine-props="MenuIptPropsPathEnum.DISABLED_SWITCH"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.HIDDEN]">
        <StnIpt
          :value="!form[MenuObjPropNameEnum.HIDDEN]"
          @update:value="() => form[MenuObjPropNameEnum.HIDDEN] = !form[MenuObjPropNameEnum.HIDDEN]"
          :type="IptTypeConst.SWITCH"
          :predefine-props="MenuIptPropsPathEnum.HIDDEN_SWITCH"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.CACHED]">
        <StnIpt
          v-model:value="form[MenuObjPropNameEnum.CACHED]"
          :type="IptTypeConst.SWITCH"
          :predefine-props="MenuIptPropsPathEnum.CACHED_SWITCH"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.LABELED]">
        <StnIpt
          v-model:value="form[MenuObjPropNameEnum.LABELED]"
          :type="IptTypeConst.SWITCH"
          :predefine-props="MenuIptPropsPathEnum.LABELED_SWITCH"
        />
      </n-gi>
      <n-gi v-if="formItemShowed[MenuObjPropNameEnum.LABEL_SORT]">
        <StnIpt
          v-model:value="form[MenuObjPropNameEnum.LABEL_SORT]"
          :type="IptTypeConst.INPUT_NUMBER"
          :predefine-props="MenuIptPropsPathEnum.LABEL_SORT"
        />
      </n-gi>
    </ResponsiveDialogForm>
  </div>
</template>
